/* eslint-disable max-statements */ import type { MDXComponents } from 'mdx/types'; import type { GetStaticPaths, GetStaticProps } from 'next'; import dynamic from 'next/dynamic'; import Head from 'next/head'; import { useRouter } from 'next/router'; import Script from 'next/script'; import type { ComponentType, HTMLAttributes } from 'react'; import { useIntl } from 'react-intl'; import { Code, Gallery, getLayout, Link, Overview, type OverviewMeta, PageLayout, ResponsiveImage, type ResponsiveImageProps, Sharing, SocialLink, Spinner, type MetaData, Heading, List, ListItem, } from '../../components'; import styles from '../../styles/pages/project.module.scss'; import type { NextPageWithLayout, ProjectPreview, Repos } from '../../types'; import { ROUTES } from '../../utils/constants'; import { getSchemaJson, getSinglePageSchema, getWebPageSchema, } from '../../utils/helpers'; import { getProjectData, getProjectFilenames, loadTranslation, type Messages, } from '../../utils/helpers/server'; import { useBreadcrumb, useGithubApi, useSettings } from '../../utils/hooks'; const BorderedImage = (props: ResponsiveImageProps) => ( ); const H1 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H2 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H3 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H4 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H5 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const H6 = ({ children = '', ...props }: HTMLAttributes) => ( {children} ); const OrderedList = ({ children, ...props }: HTMLAttributes) => ( {children} ); const UnorderedList = ({ children, ...props }: HTMLAttributes) => ( {children} ); const components: MDXComponents = { Code, Gallery, h1: H1, h2: H2, h3: H3, h4: H4, h5: H5, h6: H6, Image: BorderedImage, li: ListItem, Link, ol: OrderedList, ul: UnorderedList, }; type ProjectPageProps = { project: ProjectPreview; translation: Messages; }; /** * Project page. */ const ProjectPage: NextPageWithLayout = ({ project }) => { const { id, intro, meta, title } = project; const { cover, dates, license, repos, seo, technologies } = meta; const intl = useIntl(); const { items: breadcrumbItems, schema: breadcrumbSchema } = useBreadcrumb({ title, url: `${ROUTES.PROJECTS}/${id}`, }); const ProjectContent: ComponentType = dynamic( async () => import(`../../content/projects/${id}.mdx`), { loading: () => , } ); const { website } = useSettings(); const { asPath } = useRouter(); const page = { title: `${seo.title} - ${website.name}`, url: `${website.url}${asPath}`, }; const headerMeta: MetaData = { publication: { date: dates.publication }, update: dates.update && dates.update !== dates.publication ? { date: dates.update } : undefined, }; /** * Retrieve the repositories links. * * @param {Repos} repositories - A repositories object. * @returns {JSX.Element[]} - An array of SocialLink. */ const getReposLinks = (repositories: Repos): JSX.Element[] => { const links = []; const githubLabel = intl.formatMessage({ defaultMessage: 'Github profile', description: 'ProjectsPage: Github profile link', id: 'Nx8Jo5', }); const gitlabLabel = intl.formatMessage({ defaultMessage: 'Gitlab profile', description: 'ProjectsPage: Gitlab profile link', id: 'sECHDg', }); if (repositories.github) links.push( ); if (repositories.gitlab) links.push( ); return links; }; const loadingRepoPopularity = intl.formatMessage({ defaultMessage: 'Loading the repository popularity...', description: 'ProjectsPage: loading repository popularity', id: 'RwI3B9', }); const { isError, isLoading, data } = useGithubApi( /* * Repo should be defined for each project so for now it is safe for my * use-case. However, I should refactored it to handle cases where it is * not defined. The logic should be extracted in another component I think. * * TODO: fix this hardly readable argument */ meta.repos ? meta.repos.github ?? '' : '' ); if (isError) return 'Error'; if (isLoading || !data) return ; const getRepoPopularity = (repo: string) => { const stars = intl.formatMessage( { defaultMessage: '{starsCount, plural, =0 {No stars on Github} one {# star on Github} other {# stars on Github}}', description: 'ProjectsPage: Github stars count', id: 'sI7gJK', }, { starsCount: data.stargazers_count } ); const popularityUrl = `https://github.com/${repo}/stargazers`; return ( <> ⭐  {stars} ); }; const overviewData: OverviewMeta = { creation: { date: data.created_at }, update: { date: data.updated_at }, license, popularity: repos?.github && getRepoPopularity(repos.github), repositories: repos ? getReposLinks(repos) : undefined, technologies, }; const webpageSchema = getWebPageSchema({ description: seo.description, locale: website.locales.default, slug: asPath, title: seo.title, updateDate: dates.update, }); const articleSchema = getSinglePageSchema({ cover: `/projects/${id}.jpg`, dates, description: intro, id: 'project', kind: 'page', locale: website.locales.default, slug: asPath, title, }); const schemaJsonLd = getSchemaJson([webpageSchema, articleSchema]); return ( <> {page.title} {/*eslint-disable-next-line react/jsx-no-literals -- Name allowed */} {/*eslint-disable-next-line react/jsx-no-literals -- Content allowed */}